/*-------------------------------------------------------------------------
 *
 * pg_proc.c
 *	  支持pg_proc关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_proc.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "executor/functions.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_type.h"
#include "pgstat.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/rel.h"
#include "utils/syscache.h"


typedef struct
{
	char	   *proname;
	char	   *prosrc;
} parse_error_callback_arg;

static void fc_sql_function_parse_error_callback(void *fc_arg);
static int	fc_match_prosrc_to_query(const char *fc_prosrc, const char *fc_queryText,
								  int fc_cursorpos);
static bool fc_match_prosrc_to_literal(const char *fc_prosrc, const char *fc_literal,
									int fc_cursorpos, int *fc_newcursorpos);


/* ----------------------------------------------------------------
 *		ProcedureCreate
 *
 * 注意： allParameterTypes、parameterModes、parameterNames、trftypes 和 proconfig
 * 要么是正确类型的数组，要么为 NULL。 我们将它们声明为 Datum，
 * 而不是 "ArrayType *"，以避免在 pg_proc.h 中导入 array.h。
 * ----------------------------------------------------------------
 */
ObjectAddress ProcedureCreate(const char *fc_procedureName,
				Oid fc_procNamespace,
				bool fc_replace,
				bool fc_returnsSet,
				Oid fc_returnType,
				Oid fc_proowner,
				Oid fc_languageObjectId,
				Oid fc_languageValidator,
				const char *fc_prosrc,
				const char *fc_probin,
				Node *fc_prosqlbody,
				char fc_prokind,
				bool fc_security_definer,
				bool fc_isLeakProof,
				bool fc_isStrict,
				char fc_volatility,
				char fc_parallel,
				oidvector *fc_parameterTypes,
				Datum fc_allParameterTypes,
				Datum fc_parameterModes,
				Datum fc_parameterNames,
				List *fc_parameterDefaults,
				Datum fc_trftypes,
				Datum fc_proconfig,
				Oid fc_prosupport,
				float4 fc_procost,
				float4 fc_prorows)
{
	Oid			fc_retval;
	int			fc_parameterCount;
	int			fc_allParamCount;
	Oid		   *fc_allParams;
	char	   *fc_paramModes = NULL;
	Oid			fc_variadicType = InvalidOid;
	Acl		   *fc_proacl = NULL;
	Relation	fc_rel;
	HeapTuple	fc_tup;
	HeapTuple	fc_oldtup;
	bool		fc_nulls[Natts_pg_proc];
	Datum		fc_values[Natts_pg_proc];
	bool		fc_replaces[Natts_pg_proc];
	NameData	fc_procname;
	TupleDesc	fc_tupDesc;
	bool		fc_is_update;
	ObjectAddress fc_myself,
				fc_referenced;
	char	   *fc_detailmsg;
	int			fc_i;
	Oid			fc_trfid;
	ObjectAddresses *fc_addrs;

	/*
	 * 健康检查
	 */
	Assert(PointerIsValid(fc_prosrc));

	fc_parameterCount = fc_parameterTypes->dim1;
	if (fc_parameterCount < 0 || fc_parameterCount > FUNC_MAX_ARGS)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
				 errmsg_plural("functions cannot have more than %d argument",
							   "functions cannot have more than %d arguments",
							   FUNC_MAX_ARGS,
							   FUNC_MAX_ARGS)));
	/* 注意：以上是正确的，我们不计算输出参数 */

	/* 解构数组输入 */
	if (fc_allParameterTypes != PointerGetDatum(NULL))
	{
		/*
		 * 我们期待数组是一个 1-D OID 数组；验证这一点。我们不
		 * 需要使用 deconstruct_array()，因为数组数据看起来就像
		 * 一个 C 数组的 OID 值。
		 */
		ArrayType  *fc_allParamArray = (ArrayType *) DatumGetPointer(fc_allParameterTypes);

		fc_allParamCount = ARR_DIMS(fc_allParamArray)[0];
		if (ARR_NDIM(fc_allParamArray) != 1 ||
			fc_allParamCount <= 0 ||
			ARR_HASNULL(fc_allParamArray) ||
			ARR_ELEMTYPE(fc_allParamArray) != OIDOID)
			elog(ERROR, "allParameterTypes is not a 1-D Oid array");
		fc_allParams = (Oid *) ARR_DATA_PTR(fc_allParamArray);
		Assert(fc_allParamCount >= fc_parameterCount);
		/* 我们假设调用者内容是正确的 */
	}
	else
	{
		fc_allParamCount = fc_parameterCount;
		fc_allParams = fc_parameterTypes->values;
	}

	if (fc_parameterModes != PointerGetDatum(NULL))
	{
		/*
		 * 我们期待数组是一个 1-D CHAR 数组；验证这一点。我们不
		 * 需要使用 deconstruct_array()，因为数组数据看起来就像
		 * 一个 C 数组的字符值。
		 */
		ArrayType  *fc_modesArray = (ArrayType *) DatumGetPointer(fc_parameterModes);

		if (ARR_NDIM(fc_modesArray) != 1 ||
			ARR_DIMS(fc_modesArray)[0] != fc_allParamCount ||
			ARR_HASNULL(fc_modesArray) ||
			ARR_ELEMTYPE(fc_modesArray) != CHAROID)
			elog(ERROR, "parameterModes is not a 1-D char array");
		fc_paramModes = (char *) ARR_DATA_PTR(fc_modesArray);
	}

	/*
	 * 不允许多态返回类型，除非有一个多态输入参数
	 * 我们可以用来推断实际返回类型。
	 */
	fc_detailmsg = check_valid_polymorphic_signature(fc_returnType,
												  fc_parameterTypes->values,
												  fc_parameterCount);
	if (fc_detailmsg)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("cannot determine result data type"),
				 errdetail_internal("%s", fc_detailmsg)));

	/*
	 * 此外，除非至少有一个输入参数是内部的，否则不允许返回类型 INTERNAL。
	 */
	fc_detailmsg = check_valid_internal_signature(fc_returnType,
											   fc_parameterTypes->values,
											   fc_parameterCount);
	if (fc_detailmsg)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("unsafe use of pseudo-type \"internal\""),
				 errdetail_internal("%s", fc_detailmsg)));

	/*
	 * 将相同的测试应用于任何 OUT 参数。
	 */
	if (fc_allParameterTypes != PointerGetDatum(NULL))
	{
		for (fc_i = 0; fc_i < fc_allParamCount; fc_i++)
		{
			if (fc_paramModes == NULL ||
				fc_paramModes[fc_i] == PROARGMODE_IN ||
				fc_paramModes[fc_i] == PROARGMODE_VARIADIC)
				continue;		/* 忽略仅输入的参数 */

			fc_detailmsg = check_valid_polymorphic_signature(fc_allParams[fc_i],
														  fc_parameterTypes->values,
														  fc_parameterCount);
			if (fc_detailmsg)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("cannot determine result data type"),
						 errdetail_internal("%s", fc_detailmsg)));
			fc_detailmsg = check_valid_internal_signature(fc_allParams[fc_i],
													   fc_parameterTypes->values,
													   fc_parameterCount);
			if (fc_detailmsg)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("unsafe use of pseudo-type \"internal\""),
						 errdetail_internal("%s", fc_detailmsg)));
		}
	}

	/* 确定可变参数类型（如果有） */
	if (fc_paramModes != NULL)
	{
		/*
		 * 只有最后一个输入参数可以是可变参数；如果是，则保存其
		 * 元素类型。这方面的错误只是 elog，因为调用者应已
		 * 检查过此项。
		 */
		for (fc_i = 0; fc_i < fc_allParamCount; fc_i++)
		{
			switch (fc_paramModes[fc_i])
			{
				case PROARGMODE_IN:
				case PROARGMODE_INOUT:
					if (OidIsValid(fc_variadicType))
						elog(ERROR, "variadic parameter must be last");
					break;
				case PROARGMODE_OUT:
					if (OidIsValid(fc_variadicType) && fc_prokind == PROKIND_PROCEDURE)
						elog(ERROR, "variadic parameter must be last");
					break;
				case PROARGMODE_TABLE:
					/* 可以 */
					break;
				case PROARGMODE_VARIADIC:
					if (OidIsValid(fc_variadicType))
						elog(ERROR, "variadic parameter must be last");
					switch (fc_allParams[fc_i])
					{
						case ANYOID:
							fc_variadicType = ANYOID;
							break;
						case ANYARRAYOID:
							fc_variadicType = ANYELEMENTOID;
							break;
						case ANYCOMPATIBLEARRAYOID:
							fc_variadicType = ANYCOMPATIBLEOID;
							break;
						default:
							fc_variadicType = get_element_type(fc_allParams[fc_i]);
							if (!OidIsValid(fc_variadicType))
								elog(ERROR, "variadic parameter is not an array");
							break;
					}
					break;
				default:
					elog(ERROR, "invalid parameter mode '%c'", fc_paramModes[fc_i]);
					break;
			}
		}
	}

	/*
	 * 一切看起来都没问题；准备要插入 pg_proc 的数据。
	 */

	for (fc_i = 0; fc_i < Natts_pg_proc; ++fc_i)
	{
		fc_nulls[fc_i] = false;
		fc_values[fc_i] = (Datum) 0;
		fc_replaces[fc_i] = true;
	}

	namestrcpy(&fc_procname, fc_procedureName);
	fc_values[Anum_pg_proc_proname - 1] = NameGetDatum(&fc_procname);
	fc_values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(fc_procNamespace);
	fc_values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(fc_proowner);
	fc_values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(fc_languageObjectId);
	fc_values[Anum_pg_proc_procost - 1] = Float4GetDatum(fc_procost);
	fc_values[Anum_pg_proc_prorows - 1] = Float4GetDatum(fc_prorows);
	fc_values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(fc_variadicType);
	fc_values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(fc_prosupport);
	fc_values[Anum_pg_proc_prokind - 1] = CharGetDatum(fc_prokind);
	fc_values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(fc_security_definer);
	fc_values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(fc_isLeakProof);
	fc_values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(fc_isStrict);
	fc_values[Anum_pg_proc_proretset - 1] = BoolGetDatum(fc_returnsSet);
	fc_values[Anum_pg_proc_provolatile - 1] = CharGetDatum(fc_volatility);
	fc_values[Anum_pg_proc_proparallel - 1] = CharGetDatum(fc_parallel);
	fc_values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(fc_parameterCount);
	fc_values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(fc_parameterDefaults));
	fc_values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(fc_returnType);
	fc_values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(fc_parameterTypes);
	if (fc_allParameterTypes != PointerGetDatum(NULL))
		fc_values[Anum_pg_proc_proallargtypes - 1] = fc_allParameterTypes;
	else
		fc_nulls[Anum_pg_proc_proallargtypes - 1] = true;
	if (fc_parameterModes != PointerGetDatum(NULL))
		fc_values[Anum_pg_proc_proargmodes - 1] = fc_parameterModes;
	else
		fc_nulls[Anum_pg_proc_proargmodes - 1] = true;
	if (fc_parameterNames != PointerGetDatum(NULL))
		fc_values[Anum_pg_proc_proargnames - 1] = fc_parameterNames;
	else
		fc_nulls[Anum_pg_proc_proargnames - 1] = true;
	if (fc_parameterDefaults != NIL)
		fc_values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(fc_parameterDefaults));
	else
		fc_nulls[Anum_pg_proc_proargdefaults - 1] = true;
	if (fc_trftypes != PointerGetDatum(NULL))
		fc_values[Anum_pg_proc_protrftypes - 1] = fc_trftypes;
	else
		fc_nulls[Anum_pg_proc_protrftypes - 1] = true;
	fc_values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(fc_prosrc);
	if (fc_probin)
		fc_values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(fc_probin);
	else
		fc_nulls[Anum_pg_proc_probin - 1] = true;
	if (fc_prosqlbody)
		fc_values[Anum_pg_proc_prosqlbody - 1] = CStringGetTextDatum(nodeToString(fc_prosqlbody));
	else
		fc_nulls[Anum_pg_proc_prosqlbody - 1] = true;
	if (fc_proconfig != PointerGetDatum(NULL))
		fc_values[Anum_pg_proc_proconfig - 1] = fc_proconfig;
	else
		fc_nulls[Anum_pg_proc_proconfig - 1] = true;
	/* proacl 将稍后确定 */

	fc_rel = table_open(ProcedureRelationId, RowExclusiveLock);
	fc_tupDesc = RelationGetDescr(fc_rel);

	/* 检查是否存在预定义 */
	fc_oldtup = SearchSysCache3(PROCNAMEARGSNSP,
							 PointerGetDatum(fc_procedureName),
							 PointerGetDatum(fc_parameterTypes),
							 ObjectIdGetDatum(fc_procNamespace));

	if (HeapTupleIsValid(fc_oldtup))
	{
		/* 存在一个；可以替换它吗？ */
		Form_pg_proc fc_oldproc = (Form_pg_proc) GETSTRUCT(fc_oldtup);
		Datum		fc_proargnames;
		bool		fc_isnull;
		const char *fc_dropcmd;

		if (!fc_replace)
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_FUNCTION),
					 errmsg("function \"%s\" already exists with same argument types",
							fc_procedureName)));
		if (!pg_proc_ownercheck(fc_oldproc->oid, fc_proowner))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
						   fc_procedureName);

		/* 不允许更改例程类型 */
		if (fc_oldproc->prokind != fc_prokind)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("cannot change routine kind"),
					 (fc_oldproc->prokind == PROKIND_AGGREGATE ?
					  errdetail("\"%s\" is an aggregate function.", fc_procedureName) :
					  fc_oldproc->prokind == PROKIND_FUNCTION ?
					  errdetail("\"%s\" is a function.", fc_procedureName) :
					  fc_oldproc->prokind == PROKIND_PROCEDURE ?
					  errdetail("\"%s\" is a procedure.", fc_procedureName) :
					  fc_oldproc->prokind == PROKIND_WINDOW ?
					  errdetail("\"%s\" is a window function.", fc_procedureName) :
					  0)));

		fc_dropcmd = (fc_prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" :
				   fc_prokind == PROKIND_AGGREGATE ? "DROP AGGREGATE" :
				   "DROP FUNCTION");

		/*
		 * 不允许更改现有 proc 的返回类型，因为
		 * 现有的规则、视图等可能依赖于返回类型。
		 *
		 * 在过程的情况下，返回类型的变化意味着
		 * 该过程的输出参数是否发生了变化。由于没有
		 * 用户可见的返回类型，我们产生一个更具体的错误消息。
		 */
		if (fc_returnType != fc_oldproc->prorettype ||
			fc_returnsSet != fc_oldproc->proretset)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 fc_prokind == PROKIND_PROCEDURE
					 ? errmsg("cannot change whether a procedure has output parameters")
					 : errmsg("cannot change return type of existing function"),

			/*
			 * 翻译者：第一个 %s 是 DROP FUNCTION、DROP PROCEDURE 或 DROP
			 * AGGREGATE
			 */
					 errhint("Use %s %s first.",
							 fc_dropcmd,
							 format_procedure(fc_oldproc->oid))));

		/*
		 * 如果返回 RECORD，检查由 OUT 参数暗示的记录类型可能的变化
		 */
		if (fc_returnType == RECORDOID)
		{
			TupleDesc	fc_olddesc;
			TupleDesc	fc_newdesc;

			fc_olddesc = build_function_result_tupdesc_t(fc_oldtup);
			fc_newdesc = build_function_result_tupdesc_d(fc_prokind,
													  fc_allParameterTypes,
													  fc_parameterModes,
													  fc_parameterNames);
			if (fc_olddesc == NULL && fc_newdesc == NULL)
				 /* 好的，两者都是运行时定义的 RECORD */ ;
			else if (fc_olddesc == NULL || fc_newdesc == NULL ||
					 !equalTupleDescs(fc_olddesc, fc_newdesc))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("cannot change return type of existing function"),
						 errdetail("Row type defined by OUT parameters is different."),
				/* 翻译者：第一个 %s 是 DROP FUNCTION 或 DROP PROCEDURE */
						 errhint("Use %s %s first.",
								 fc_dropcmd,
								 format_procedure(fc_oldproc->oid))));
		}

		/*
		 * 如果有任何命名输入参数，检查确保名称未被更改，因为这可能会破坏现有调用。我们
		 * 允许为以前未命名的参数添加名称。
		 */
		fc_proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, fc_oldtup,
									  Anum_pg_proc_proargnames,
									  &fc_isnull);
		if (!fc_isnull)
		{
			Datum		fc_proargmodes;
			char	  **fc_old_arg_names;
			char	  **fc_new_arg_names;
			int			fc_n_old_arg_names;
			int			fc_n_new_arg_names;
			int			fc_j;

			fc_proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, fc_oldtup,
										  Anum_pg_proc_proargmodes,
										  &fc_isnull);
			if (fc_isnull)
				fc_proargmodes = PointerGetDatum(NULL);	/* 只是为了确保 */

			fc_n_old_arg_names = get_func_input_arg_names(fc_proargnames,
													   fc_proargmodes,
													   &fc_old_arg_names);
			fc_n_new_arg_names = get_func_input_arg_names(fc_parameterNames,
													   fc_parameterModes,
													   &fc_new_arg_names);
			for (fc_j = 0; fc_j < fc_n_old_arg_names; fc_j++)
			{
				if (fc_old_arg_names[fc_j] == NULL)
					continue;
				if (fc_j >= fc_n_new_arg_names || fc_new_arg_names[fc_j] == NULL ||
					strcmp(fc_old_arg_names[fc_j], fc_new_arg_names[fc_j]) != 0)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("cannot change name of input parameter \"%s\"",
									fc_old_arg_names[fc_j]),
					/* 翻译者：第一个 %s 是 DROP FUNCTION 或 DROP PROCEDURE */
							 errhint("Use %s %s first.",
									 fc_dropcmd,
									 format_procedure(fc_oldproc->oid))));
			}
		}

		/*
		 * 如果有现有默认值，检查兼容性：重新定义
		 * 不得删除任何默认值，也不得更改它们的类型。 （删除默认值
		 * 可能导致函数无法满足现有调用。
		 * 更改类型仅在相关参数是多态的情况下可能，并且在这种情况下，默认类型的变化可能会改变
		 * 现有调用的解析输出类型。）
		 */
		if (fc_oldproc->pronargdefaults != 0)
		{
			Datum		fc_proargdefaults;
			List	   *fc_oldDefaults;
			ListCell   *fc_oldlc;
			ListCell   *fc_newlc;

			if (list_length(fc_parameterDefaults) < fc_oldproc->pronargdefaults)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("cannot remove parameter defaults from existing function"),
				/* 翻译者：第一个 %s 是 DROP FUNCTION 或 DROP PROCEDURE */
						 errhint("Use %s %s first.",
								 fc_dropcmd,
								 format_procedure(fc_oldproc->oid))));

			fc_proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, fc_oldtup,
											 Anum_pg_proc_proargdefaults,
											 &fc_isnull);
			Assert(!fc_isnull);
			fc_oldDefaults = castNode(List, stringToNode(TextDatumGetCString(fc_proargdefaults)));
			Assert(list_length(fc_oldDefaults) == fc_oldproc->pronargdefaults);

			/* 新列表可以有比旧列表更多的默认值，向前推进它们 */
			fc_newlc = list_nth_cell(fc_parameterDefaults,
								  list_length(fc_parameterDefaults) -
								  fc_oldproc->pronargdefaults);

			foreach(fc_oldlc, fc_oldDefaults)
			{
				Node	   *fc_oldDef = (Node *) lfirst(fc_oldlc);
				Node	   *fc_newDef = (Node *) lfirst(fc_newlc);

				if (exprType(fc_oldDef) != exprType(fc_newDef))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("cannot change data type of existing parameter default value"),
					/* 翻译者：第一个 %s 是 DROP FUNCTION 或 DROP PROCEDURE */
							 errhint("Use %s %s first.",
									 fc_dropcmd,
									 format_procedure(fc_oldproc->oid))));
				fc_newlc = lnext(fc_parameterDefaults, fc_newlc);
			}
		}

		/*
		 * 也不要更改现有的 oid、所有权或权限。 注意
		 * 下面的依赖更新代码必须与这一决定一致。
		 */
		fc_replaces[Anum_pg_proc_oid - 1] = false;
		fc_replaces[Anum_pg_proc_proowner - 1] = false;
		fc_replaces[Anum_pg_proc_proacl - 1] = false;

		/* 好的，执行... */
		fc_tup = heap_modify_tuple(fc_oldtup, fc_tupDesc, fc_values, fc_nulls, fc_replaces);
		CatalogTupleUpdate(fc_rel, &fc_tup->t_self, fc_tup);

		ReleaseSysCache(fc_oldtup);
		fc_is_update = true;
	}
	else
	{
		/* 创建一个新过程 */
		Oid			fc_newOid;

		/* 首先，获取默认权限并设置 proacl */
		fc_proacl = get_user_default_acl(OBJECT_FUNCTION, fc_proowner,
									  fc_procNamespace);
		if (fc_proacl != NULL)
			fc_values[Anum_pg_proc_proacl - 1] = PointerGetDatum(fc_proacl);
		else
			fc_nulls[Anum_pg_proc_proacl - 1] = true;

		fc_newOid = GetNewOidWithIndex(fc_rel, ProcedureOidIndexId,
									Anum_pg_proc_oid);
		fc_values[Anum_pg_proc_oid - 1] = ObjectIdGetDatum(fc_newOid);
		fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);
		CatalogTupleInsert(fc_rel, fc_tup);
		fc_is_update = false;
	}


	fc_retval = ((Form_pg_proc) GETSTRUCT(fc_tup))->oid;

	/*
	 * 为新函数创建依赖关系。如果我们在更新一个
	 * 已存在的函数，首先删除任何现有的 pg_depend 条目。
	 * （不过，由于我们没有更改所有权或权限， 
	 * 共享依赖关系不需要改变，我们就不动它们。）
	 */
	if (fc_is_update)
		deleteDependencyRecordsFor(ProcedureRelationId, fc_retval, true);

	fc_addrs = new_object_addresses();

	ObjectAddressSet(fc_myself, ProcedureRelationId, fc_retval);

	/* 对命名空间的依赖 */
	ObjectAddressSet(fc_referenced, NamespaceRelationId, fc_procNamespace);
	add_exact_object_address(&fc_referenced, fc_addrs);

	/* 对实现语言的依赖 */
	ObjectAddressSet(fc_referenced, LanguageRelationId, fc_languageObjectId);
	add_exact_object_address(&fc_referenced, fc_addrs);

	/* 对返回类型的依赖 */
	ObjectAddressSet(fc_referenced, TypeRelationId, fc_returnType);
	add_exact_object_address(&fc_referenced, fc_addrs);

	/* 对返回类型所使用的转换的依赖（如有） */
	if ((fc_trfid = get_transform_oid(fc_returnType, fc_languageObjectId, true)))
	{
		ObjectAddressSet(fc_referenced, TransformRelationId, fc_trfid);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 对参数类型的依赖 */
	for (fc_i = 0; fc_i < fc_allParamCount; fc_i++)
	{
		ObjectAddressSet(fc_referenced, TypeRelationId, fc_allParams[fc_i]);
		add_exact_object_address(&fc_referenced, fc_addrs);

		/* 对参数类型所使用的转换的依赖（如有） */
		if ((fc_trfid = get_transform_oid(fc_allParams[fc_i], fc_languageObjectId, true)))
		{
			ObjectAddressSet(fc_referenced, TransformRelationId, fc_trfid);
			add_exact_object_address(&fc_referenced, fc_addrs);
		}
	}

	/* 对支持函数的依赖（如有） */
	if (OidIsValid(fc_prosupport))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_prosupport);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	record_object_address_dependencies(&fc_myself, fc_addrs, DEPENDENCY_NORMAL);
	free_object_addresses(fc_addrs);

	/* 对 SQL 程序体的依赖 */
	if (fc_languageObjectId == SQLlanguageId && fc_prosqlbody)
		recordDependencyOnExpr(&fc_myself, fc_prosqlbody, NIL, DEPENDENCY_NORMAL);

	/* 对参数默认表达式的依赖 */
	if (fc_parameterDefaults)
		recordDependencyOnExpr(&fc_myself, (Node *) fc_parameterDefaults,
							   NIL, DEPENDENCY_NORMAL);

	/* 对所有者的依赖 */
	if (!fc_is_update)
		recordDependencyOnOwner(ProcedureRelationId, fc_retval, fc_proowner);

	/* 对在 ACL 中提到的任何角色的依赖 */
	if (!fc_is_update)
		recordDependencyOnNewAcl(ProcedureRelationId, fc_retval, 0,
								 fc_proowner, fc_proacl);

	/* 对扩展的依赖 */
	recordDependencyOnCurrentExtension(&fc_myself, fc_is_update);

	heap_freetuple(fc_tup);

	/* 新函数的创建后钩子 */
	InvokeObjectPostCreateHook(ProcedureRelationId, fc_retval, 0);

	table_close(fc_rel, RowExclusiveLock);

	/* 验证函数体 */
	if (OidIsValid(fc_languageValidator))
	{
		ArrayType  *set_items = NULL;
		int			fc_save_nestlevel = 0;

		/* 增加命令计数器，以便新的元组可以被验证器看到 */
		CommandCounterIncrement();

		/*
		 * 设置每个函数的配置参数，以便在函数期望的
		 * 环境中进行验证。然而，如果 check_function_bodies 关闭，
		 * 我们就不这样做，因为那会造成 pg_dump 无法处理的
		 * 转储顺序问题。（例如，SET 子句可能会引用尚未创建的
		 * 文本搜索配置。）这意味着当 check_function_bodies 关闭时，
		 * 验证器不应对可能依赖于 GUC 参数的任何事项发出警告。
		 */
		if (check_function_bodies)
		{
			set_items = (ArrayType *) DatumGetPointer(fc_proconfig);
			if (set_items)		/* 需要一个新的 GUC 嵌套级别 */
			{
				fc_save_nestlevel = NewGUCNestLevel();
				ProcessGUCArray(set_items,
								(superuser() ? PGC_SUSET : PGC_USERSET),
								PGC_S_SESSION,
								GUC_ACTION_SAVE);
			}
		}

		OidFunctionCall1(fc_languageValidator, ObjectIdGetDatum(fc_retval));

		if (set_items)
			AtEOXact_GUC(true, fc_save_nestlevel);
	}

	/* 确保在事务中止时统计信息被删除 */
	if (!fc_is_update)
		pgstat_create_function(fc_retval);

	return fc_myself;
}



/*
 * 内部函数的验证器
 *
 * 检查给定的内部函数名称（"prosrc" 值）是否为
 * 已知的内置函数。
 */
Datum fmgr_internal_validator(PG_FUNCTION_ARGS)
{
	Oid			fc_funcoid = PG_GETARG_OID(0);
	HeapTuple	fc_tuple;
	bool		fc_isnull;
	Datum		fc_tmp;
	char	   *fc_prosrc;

	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, fc_funcoid))
		PG_RETURN_VOID();

	/*
	 * 我们不支持 check_function_bodies，因为如果函数名称
	 * 现在不存在，那么以后很可能也找不到。
	 */

	fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_funcoid));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for function %u", fc_funcoid);

	fc_tmp = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_prosrc, &fc_isnull);
	if (fc_isnull)
		elog(ERROR, "null prosrc");
	fc_prosrc = TextDatumGetCString(fc_tmp);

	if (fmgr_internal_function(fc_prosrc) == InvalidOid)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("there is no built-in function named \"%s\"",
						fc_prosrc)));

	ReleaseSysCache(fc_tuple);

	PG_RETURN_VOID();
}



/*
 * C 语言函数的验证器
 *
 * 确保库文件存在、可加载，并包含
 * 指定的链接符号。同时检查有效的函数
 * 信息记录。
 */
Datum fmgr_c_validator(PG_FUNCTION_ARGS)
{
	Oid			fc_funcoid = PG_GETARG_OID(0);
	void	   *fc_libraryhandle;
	HeapTuple	fc_tuple;
	bool		fc_isnull;
	Datum		fc_tmp;
	char	   *fc_prosrc;
	char	   *fc_probin;

	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, fc_funcoid))
		PG_RETURN_VOID();

	/*
	 * 如果 !check_function_bodies，跳过检查将最一致，
	 * 但该开关的目的是为了帮助 pg_dump 加载，
	 * 对于 pg_dump 加载，进行检查要好得多。
	 */

	fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_funcoid));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for function %u", fc_funcoid);

	fc_tmp = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_prosrc, &fc_isnull);
	if (fc_isnull)
		elog(ERROR, "null prosrc for C function %u", fc_funcoid);
	fc_prosrc = TextDatumGetCString(fc_tmp);

	fc_tmp = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_probin, &fc_isnull);
	if (fc_isnull)
		elog(ERROR, "null probin for C function %u", fc_funcoid);
	fc_probin = TextDatumGetCString(fc_tmp);

	(void) load_external_function(fc_probin, fc_prosrc, true, &fc_libraryhandle);
	(void) fetch_finfo_record(fc_libraryhandle, fc_prosrc);

	ReleaseSysCache(fc_tuple);

	PG_RETURN_VOID();
}


/*
 * SQL 语言函数的验证器
 *
 * 在这里解析，以确保它没有语法错误。
 */
Datum fmgr_sql_validator(PG_FUNCTION_ARGS)
{
	Oid			fc_funcoid = PG_GETARG_OID(0);
	HeapTuple	fc_tuple;
	Form_pg_proc fc_proc;
	List	   *fc_raw_parsetree_list;
	List	   *fc_querytree_list;
	ListCell   *fc_lc;
	bool		fc_isnull;
	Datum		fc_tmp;
	char	   *fc_prosrc;
	parse_error_callback_arg fc_callback_arg;
	ErrorContextCallback fc_sqlerrcontext;
	bool		fc_haspolyarg;
	int			fc_i;

	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, fc_funcoid))
		PG_RETURN_VOID();

	fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_funcoid));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for function %u", fc_funcoid);
	fc_proc = (Form_pg_proc) GETSTRUCT(fc_tuple);

	/* 不允许伪类型结果 */
	/* 除 RECORD、VOID 或多态类型外 */
	if (get_typtype(fc_proc->prorettype) == TYPTYPE_PSEUDO &&
		fc_proc->prorettype != RECORDOID &&
		fc_proc->prorettype != VOIDOID &&
		!IsPolymorphicType(fc_proc->prorettype))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("SQL functions cannot return type %s",
						format_type_be(fc_proc->prorettype))));

	/* 不允许在参数中使用伪类型 */
	/* 除多态类型外 */
	fc_haspolyarg = false;
	for (fc_i = 0; fc_i < fc_proc->pronargs; fc_i++)
	{
		if (get_typtype(fc_proc->proargtypes.values[fc_i]) == TYPTYPE_PSEUDO)
		{
			if (IsPolymorphicType(fc_proc->proargtypes.values[fc_i]))
				fc_haspolyarg = true;
			else
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("SQL functions cannot have arguments of type %s",
								format_type_be(fc_proc->proargtypes.values[fc_i]))));
		}
	}

	/* 如果!check_function_bodies则推迟主体检查 */
	if (check_function_bodies)
	{
		fc_tmp = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_prosrc, &fc_isnull);
		if (fc_isnull)
			elog(ERROR, "null prosrc");

		fc_prosrc = TextDatumGetCString(fc_tmp);

		/*
		 * 设置 ereport() 的错误追踪支持。
		 */
		fc_callback_arg.proname = NameStr(fc_proc->proname);
		fc_callback_arg.prosrc = fc_prosrc;

		fc_sqlerrcontext.callback = fc_sql_function_parse_error_callback;
		fc_sqlerrcontext.arg = (void *) &fc_callback_arg;
		fc_sqlerrcontext.previous = error_context_stack;
		error_context_stack = &fc_sqlerrcontext;

		/* 如果我们有 prosqlbody，则关注该部分而不是 prosrc */
		fc_tmp = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_prosqlbody, &fc_isnull);
		if (!fc_isnull)
		{
			Node	   *fc_n;
			List	   *fc_stored_query_list;

			fc_n = stringToNode(TextDatumGetCString(fc_tmp));
			if (IsA(fc_n, List))
				fc_stored_query_list = linitial(castNode(List, fc_n));
			else
				fc_stored_query_list = list_make1(fc_n);

			fc_querytree_list = NIL;
			foreach(fc_lc, fc_stored_query_list)
			{
				Query	   *fc_parsetree = lfirst_node(Query, fc_lc);
				List	   *fc_querytree_sublist;

				/*
				 * 通常，在解析 CREATE FUNCTION 命令的主体时，
				 * 我们会已经获取了锁。但是，验证器函数
				 * 不能假设它只在这种上下文中被调用。
				 */
				AcquireRewriteLocks(fc_parsetree, true, false);
				fc_querytree_sublist = pg_rewrite_query(fc_parsetree);
				fc_querytree_list = lappend(fc_querytree_list, fc_querytree_sublist);
			}
		}
		else
		{
			/*
			 * 如果存在任何多态输入类型，我们无法对
			 * 函数定义进行全面预检查，因为表达式结果的
			 * 实际数据类型无法解析。检查将在运行时进行。
			 *
			 * 不过，我们可以通过原始解析器运行文本；
			 * 这至少可以捕捉到愚蠢的语法错误。
			 */
			fc_raw_parsetree_list = pg_parse_query(fc_prosrc);
			fc_querytree_list = NIL;

			if (!fc_haspolyarg)
			{
				/*
				 * 可以进行全面预检查：分析并重写查询，
				 * 然后验证结果类型。
				 */
				SQLFunctionParseInfoPtr fc_pinfo;

				/* 但首先，设置参数信息 */
				fc_pinfo = prepare_sql_fn_parse_info(fc_tuple, NULL, InvalidOid);

				foreach(fc_lc, fc_raw_parsetree_list)
				{
					RawStmt    *fc_parsetree = lfirst_node(RawStmt, fc_lc);
					List	   *fc_querytree_sublist;

					fc_querytree_sublist = pg_analyze_and_rewrite_withcb(fc_parsetree,
																	  fc_prosrc,
																	  (ParserSetupHook) sql_fn_parser_setup,
																	  fc_pinfo,
																	  NULL);
					fc_querytree_list = lappend(fc_querytree_list,
											 fc_querytree_sublist);
				}
			}
		}

		if (!fc_haspolyarg)
		{
			Oid			fc_rettype;
			TupleDesc	fc_rettupdesc;

			check_sql_fn_statements(fc_querytree_list);

			(void) get_func_result_type(fc_funcoid, &fc_rettype, &fc_rettupdesc);

			(void) check_sql_fn_retval_ext(fc_querytree_list,
										   fc_rettype, fc_rettupdesc,
										   fc_proc->prokind,
										   false, NULL);
		}

		error_context_stack = fc_sqlerrcontext.previous;
	}

	ReleaseSysCache(fc_tuple);

	PG_RETURN_VOID();
}

/* 
 * SQL函数定义中处理错误的错误上下文回调 
 */
static void fc_sql_function_parse_error_callback(void *fc_arg)
{
	parse_error_callback_arg *fc_callback_arg = (parse_error_callback_arg *) fc_arg;

	/* 查看是否为语法错误；如果是，则转置到CREATE FUNCTION */
	if (!function_parse_error_transpose(fc_callback_arg->prosrc))
	{
		/* 如果不是语法错误，将信息推送到上下文栈 */
		errcontext("SQL function \"%s\"", fc_callback_arg->proname);
	}
}

/* 
 * 调整在CREATE FUNCTION或DO命令的函数体内发生的语法错误。
 * 这可以被任何函数验证器或匿名块处理程序使用，不仅限于SQL语言函数。
 * 假定语法错误位置最初相对于函数体字符串（如传入）。如果可能，我们调整位置以引用原始命令文本；如果无法做到这一点，我们设置一个“内部查询”语法错误。
 *
 * 如果处理了语法错误则返回true，如果没有则返回false。
 */
bool function_parse_error_transpose(const char *fc_prosrc)
{
	int			fc_origerrposition;
	int			fc_newerrposition;

	/* 
	 * 除非我们处理的是具有游标位置的语法错误，否则没有什么可做的。
	 *
	 * 一些PL可能更愿意将错误位置报告为一个内部错误，因此也要检查这一点。
	 */
	fc_origerrposition = geterrposition();
	if (fc_origerrposition <= 0)
	{
		fc_origerrposition = getinternalerrposition();
		if (fc_origerrposition <= 0)
			return false;
	}

	/* 我们可以从活动门户获取原始查询文本（黑客...） */
	if (ActivePortal && ActivePortal->status == PORTAL_ACTIVE)
	{
		const char *fc_queryText = ActivePortal->sourceText;

		/* 尝试在原始文本中查找prosrc */
		fc_newerrposition = fc_match_prosrc_to_query(fc_prosrc, fc_queryText,
											   fc_origerrposition);
	}
	else
	{
		/* 
		 * 如果没有ActivePortal，则静默放弃。这是一种不寻常的情况
		 * 但它可能发生在例如逻辑复制工作者中。
		 */
		fc_newerrposition = -1;
	}

	if (fc_newerrposition > 0)
	{
		/* 成功，因此修正错误位置以引用原始查询 */
		errposition(fc_newerrposition);
		/* 消除将错误报告为“内部查询”的任何记录 */
		internalerrposition(0);
		internalerrquery(NULL);
	}
	else
	{
		/* 
		 * 如果不成功，将位置转换为内部位置
		 * 标记，并将函数文本作为内部查询提供。
		 */
		errposition(0);
		internalerrposition(fc_origerrposition);
		internalerrquery(fc_prosrc);
	}

	return true;
}

/* 
 * 尝试在CREATE FUNCTION或DO命令的给定文本中找到包含函数体的字符串字面量。如果成功，返回
 * 对应于字面量内给定字符索引的命令中的字符（而不是字节）索引。如果不成功，则返回0。
 */
static int fc_match_prosrc_to_query(const char *fc_prosrc, const char *fc_queryText,
					  int fc_cursorpos)
{
	/* 
	 * 与其完全解析原始命令，我们只扫描
	 * 命令，寻找$prosrc$或'prosrc'。这可能会误导（虽然
	 * 在任何非常可能的场景中都不会），因此如果找到多个
	 * 匹配，则失败。
	 */
	int			fc_prosrclen = strlen(fc_prosrc);
	int			fc_querylen = strlen(fc_queryText);
	int			fc_matchpos = 0;
	int			fc_curpos;
	int			fc_newcursorpos;

	for (fc_curpos = 0; fc_curpos < fc_querylen - fc_prosrclen; fc_curpos++)
	{
		if (fc_queryText[fc_curpos] == '$' &&
			strncmp(fc_prosrc, &fc_queryText[fc_curpos + 1], fc_prosrclen) == 0 &&
			fc_queryText[fc_curpos + 1 + fc_prosrclen] == '$')
		{
			/* 
			 * 找到一个$foo$匹配项。由于美元引号字面量中没有嵌入的引用
			 * 字符，我们不需要做任何复杂的算术；只需按起始位置偏移。
			 */
			if (fc_matchpos)
				return 0;		/* 多个匹配项，失败 */
			fc_matchpos = pg_mbstrlen_with_len(fc_queryText, fc_curpos + 1)
				+ fc_cursorpos;
		}
		else if (fc_queryText[fc_curpos] == '\'' &&
				 fc_match_prosrc_to_literal(fc_prosrc, &fc_queryText[fc_curpos + 1],
										 fc_cursorpos, &fc_newcursorpos))
		{
			/* 
			 * 找到一个'foo'匹配项。match_prosrc_to_literal()已经调整
			 * 以适应字面量中嵌入的任何引号或反斜杠。
			 */
			if (fc_matchpos)
				return 0;		/* 多个匹配项，失败 */
			fc_matchpos = pg_mbstrlen_with_len(fc_queryText, fc_curpos + 1)
				+ fc_newcursorpos;
		}
	}

	return fc_matchpos;
}

/* 
 * 尝试将给定源文本与单引号字面量匹配。
 * 如果成功，调整newcursorpos以对应于源文本中cursorpos
 * 的字符（而不是字节）索引。
 *
 * 在输入时，literal指向一个'字符的后面。我们必须检查对应的
 * 尾部引号。
 */
static bool fc_match_prosrc_to_literal(const char *fc_prosrc, const char *fc_literal,
						int fc_cursorpos, int *fc_newcursorpos)
{
	int			fc_newcp = fc_cursorpos;
	int			fc_chlen;

	/* 
	 * 此实现处理字符串字面量中的反斜杠和重复引号。
	 * 它不处理跨行边界的字面量的SQL语法。
	 *
	 * 我们逐字符进行比较，而不是逐字节进行比较，因此
	 * 我们可以进行正确的cursorpos数学计算。
	 */
	while (*fc_prosrc)
	{
		fc_cursorpos--;			/* 游标之前剩余的字符 */

		/* 
		 * 检查字面量中的反斜杠和重复引号；当在游标之前找到时调整
		 * newcp。 */
		if (*fc_literal == '\\')
		{
			fc_literal++;
			if (fc_cursorpos > 0)
				fc_newcp++;
		}
		else if (*fc_literal == '\'')
		{
			if (fc_literal[1] != '\'')
				goto fail;
			fc_literal++;
			if (fc_cursorpos > 0)
				fc_newcp++;
		}
		fc_chlen = pg_mblen(fc_prosrc);
		if (strncmp(fc_prosrc, fc_literal, fc_chlen) != 0)
			goto fail;
		fc_prosrc += fc_chlen;
		fc_literal += fc_chlen;
	}

	if (*fc_literal == '\'' && fc_literal[1] != '\'')
	{
		/* 成功 */
		*fc_newcursorpos = fc_newcp;
		return true;
	}

fail:
	/* 必须设置*newcursorpos以抑制编译器警告 */
	*fc_newcursorpos = fc_newcp;
	return false;
}

List * oid_array_to_list(Datum fc_datum)
{
	ArrayType  *fc_array = DatumGetArrayTypeP(fc_datum);
	Datum	   *fc_values;
	int			fc_nelems;
	int			fc_i;
	List	   *fc_result = NIL;

	deconstruct_array(fc_array,
					  OIDOID,
					  sizeof(Oid), true, TYPALIGN_INT,
					  &fc_values, NULL, &fc_nelems);
	for (fc_i = 0; fc_i < fc_nelems; fc_i++)
		fc_result = lappend_oid(fc_result, fc_values[fc_i]);
	return fc_result;
}
